package simmar.xcs;

import java.io.Serializable;
import java.util.Random;

import simmar.utilities.Helpers;

public class XCSClassifier implements XCSConfig, Serializable
{
    private static final long serialVersionUID = -8379748480726815281L;
    
    Rule _rule;
    protected float _prediction;
    protected float _accuracy;
    protected float _fitness;
    protected int _experience;
    protected int _timestamp;
    protected int _actionSetSize;
    protected int _numerosity;

    // MARIO CONDITIONS
    // MarioOnGround;
    // MarioAbleToJump;
    // MarioAbleToShoot;
    // MarioCarrying;
    //

    // MARIO ACTIONS
    // KEY_LEFT = 0;
    // KEY_RIGHT = 1;
    // KEY_DOWN = 2;
    // KEY_JUMP = 3;
    // KEY_SPEED = 4;
    // KEY_UP = 5;

    // 100000 left
    // 100100 left + jump
    // 100110 left + jump + speed
    // 100010 left + speed
    // 010000 right
    // 010100 right + jump
    // 010110 right + jump + speed
    // 010010 right + speed
    // 001000 down
    // 000100 jump
    // 000001 up

    public XCSClassifier()
    {
        _rule = new Rule();
        _prediction = INITIAL_PREDICTION;
        _accuracy = INITIAL_ACCURACY;
        _fitness = INITIAL_FITNESS;
        _experience = 0;
        _timestamp = 0;
        _actionSetSize = 1;
        _numerosity = 1;
    }

    public Rule getRule()
    {
        return _rule;
    }

    public void setRule(Rule newRule)
    {
        _rule = newRule;
    }

    public float getPrediction()
    {
        return _prediction;
    }

    public void setPrediction(float newPrediction)
    {
        _prediction = newPrediction;
    }

    public float getAccuracy()
    {
        return _accuracy;
    }

    public void setAccuracy(float newAccuracy)
    {
        _accuracy = newAccuracy;
    }

    public float getFitness()
    {
        return _fitness;
    }

    public void setFitness(float newFitness)
    {
        _fitness = newFitness;
    }

    public void incrementExperience()
    {
        _experience++;
    }

    public int getExperience()
    {
        return _experience;
    }

    public void setExperience(int newExperience)
    {
        _experience = newExperience;
    }

    public int getNumerosity()
    {
        return _numerosity;
    }

    public void setNumerosity(int newNumerosity)
    {
        _numerosity = newNumerosity;
    }

    public int getTimestamp()
    {
        return _timestamp;
    }
    
    public void incrementNumerosity()
    {
        ++_numerosity;
    }

    public void decrementNumerosity()
    {
        --_numerosity;
    }

    public void setTimestamp(int newTimestamp)
    {
        _timestamp = newTimestamp;
    }

    public int getActionSetSize()
    {
        return _actionSetSize;
    }

    public void setActionSetSize(int newActionSetSize)
    {
        _actionSetSize = newActionSetSize;
    }
    
    public String toString()
    {
        String classifierString = _rule.toString() + "\n";
        classifierString += "Prediction: " + _prediction  + "\n";
        classifierString += "Accuracy: " + _accuracy + "\n";
        classifierString += "Fitness: " + _fitness + "\n";
        classifierString += "Experience: " + _experience + "\n";
        classifierString += "Numerosity: " + _numerosity + "\n";
        classifierString += "Timestamp: " + _timestamp + "\n";
        classifierString += "Action set size: " + _actionSetSize;
        
        return classifierString;
    }

    // RULE CLASS (CONDITION-ACTION PAIR)
    protected class Rule implements Serializable
    {
        private static final long serialVersionUID = 2184975165658166039L;
        protected int[] _condition;
        protected Action _action;

        public Rule()
        {
            _condition = new int[CONDITION_SIZE];
            _action = Action.UNDEFINED;

            Random rand = new Random();

            for (int i = 0; i < CONDITION_SIZE; i++)
            {
//                _condition[i] = rand.nextInt(3) - 1; // values between -1 and 1
                _condition[i] = -1; // All wildcards
            }

            _action = Helpers.intToAction(rand.nextInt(MARIO_ACTIONS.length));
        }

        public int[] getCondition()
        {
            return _condition;
        }

        public void setCondition(int[] newCondition)
        {
            _condition = newCondition;
        }

        public int getConditionElement(int index)
        {
            return _condition[index];
        }

        public void setConditionElement(int index, int newElement)
        {
            _condition[index] = newElement;
        }

        public Action getAction()
        {
            return _action;
        }

        public void setAction(Action newAction)
        {
            _action = newAction;
        }

        public int getWildcardCount()
        {
            int count = 0;

            for (int i = 0; i < CONDITION_SIZE; i++)
            {
                if (_condition[i] == -1)
                    count++;
            }

            return count;
        }

        public String toString()
        {
            String ruleString = "<";

            for (int i = 0; i < CONDITION_SIZE; i++)
            {
                if (_condition[i] == 0 || _condition[i] == 1)
                    ruleString += _condition[i];

                else if (_condition[i] == -1)
                    ruleString += "#";

                // if (i != CONDITION_SIZE - 1)
                // ruleString += " ";
            }

            ruleString += "><" + _action.toString() + ">";

            return ruleString;
        }
        
        public boolean isEqualTo(Rule otherRule)
        {
            boolean isEqual = true;
            
            for (int i = 0; i < CONDITION_SIZE; i++)
            {
                if (_condition[i] != otherRule.getCondition()[i])
                    return false;
            }
            
            if (_action != otherRule.getAction())
                return false;
                
            return isEqual;
        }
    }
}